// Copy from https://github.com/yidun/captcha-java-demo/blob/master/src/main/webapp/index.js

/* eslint-disable no-undef */
(function (global, factory) {
  "use strict";
  if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = global.document
      ? factory(global)
      : function (w) {
          if (!w.document) {
            throw new Error(
              "initNECaptchaWithFallback requires a window with a document"
            );
          }
          return factory(w);
        };
  } else if (typeof define === "function" && define.amd) {
    define("initNECaptchaWithFallback", [], function () {
      return factory(global);
    });
  } else {
    global.initNECaptchaWithFallback = factory(global);
  }
})(typeof window !== "undefined" ? window : this, function (window) {
  "use strict";
  var errorCallbackCount = 0;

  // 常量
  var DEFAULT_VALIDATE =
    "QjGAuvoHrcpuxlbw7cp4WnIbbjzG4rtSlpc7EDovNHQS._ujzPZpeCInSxIT4WunuDDh8dRZYF2GbBGWyHlC6q5uEi9x-TXT9j7J705vSsBXyTar7aqFYyUltKYJ7f4Y2TXm_1Mn6HFkb4M7URQ_rWtpxQ5D6hCgNJYC0HpRE7.2sttqYKLoi7yP1KHzK-PptdHHkVwb77cwS2EJW7Mj_PsOtnPBubTmTZLpnRECJR99dWTVC11xYG0sx8dJNLUxUFxEyzTfX4nSmQz_T5sXATRKHtVAz7nmV0De5unmflfAlUwMGKlCT1khBtewlgN5nHvyxeD8Z1_fPVzi9oznl-sbegj6lKfCWezmLcwft8.4yaVh6SlzXJq-FnSK.euq9OBd5jYc82ge2_hEca1fGU--SkPRzgwkzew4O4qjdS2utdPwFONnhKAIMJRPUmCV4lPHG1OeRDvyNV8sCnuFMw7leasxIhPoycl4pm5bNy70Z1laozEGJgItVNr3"; // 默认validate
  var FALLBACK_LANG = {
    "zh-CN": "前方拥堵，已自动跳过验证",
    en: "captcha error，Verified automatically",
  };
  var CACHE_MIN = 1000 * 60; // 缓存时长单位，1分钟
  var REQUEST_SCRIPT_ERROR = 502;

  var RESOURCE_CACHE = {};

  // 工具函数
  function loadScript(src, cb) {
    var head = document.head || document.getElementsByTagName("head")[0];
    var script = document.createElement("script");

    cb = cb || function () {};

    script.type = "text/javascript";
    script.charset = "utf8";
    script.async = true;
    script.src = src;

    if (!("onload" in script)) {
      script.onreadystatechange = function () {
        if (this.readyState !== "complete" && this.readyState !== "loaded")
          return;
        this.onreadystatechange = null;
        cb(null, script); // there is no way to catch loading errors in IE8
      };
    }

    script.onload = function () {
      this.onerror = this.onload = null;
      cb(null, script);
    };
    script.onerror = function () {
      // because even IE9 works not like others
      this.onerror = this.onload = null;
      cb(new Error("Failed to load " + this.src), script);
    };

    head.appendChild(script);
  }

  function joinUrl(protocol, host, path) {
    protocol = protocol || "";
    host = host || "";
    path = path || "";
    if (protocol) {
      protocol = protocol.replace(/:?\/{0,2}$/, "://");
    }
    if (host) {
      var matched = host.match(/^([-0-9a-zA-Z.:]*)(\/.*)?/);
      host = matched[1];
      path = (matched[2] || "") + "/" + path;
    }
    !host && (protocol = "");

    return protocol + host + path;
  }

  function setDomText(el, value) {
    if (value === undefined) return;
    var nodeType = el.nodeType;
    if (nodeType === 1 || nodeType === 11 || nodeType === 9) {
      if (typeof el.textContent === "string") {
        el.textContent = value;
      } else {
        el.innerText = value;
      }
    }
  }

  function queryAllByClassName(selector, node) {
    node = node || document;
    if (node.querySelectorAll) {
      return node.querySelectorAll(selector);
    }
    if (!/^\.[^.]+$/.test(selector)) return [];
    if (node.getElementsByClassName) {
      return node.getElementsByClassName(selector);
    }

    var children = node.getElementsByTagName("*");
    var current;
    var result = [];
    var className = selector.slice(1);
    for (var i = 0, l = children.length; i < l; i++) {
      current = children[i];
      if (~(" " + current.className + " ").indexOf(" " + className + " ")) {
        result.push(current);
      }
    }
    return result;
  }

  function assert(condition, msg) {
    if (!condition) throw new Error("[NECaptcha] " + msg);
  }

  function isInteger(val) {
    if (Number.isInteger) {
      return Number.isInteger(val);
    }
    return typeof val === "number" && isFinite(val) && Math.floor(val) === val;
  }

  function isArray(val) {
    if (Array.isArray) return Array.isArray(val);
    return Object.prototype.toString.call(val) === "[object Array]";
  }

  function ObjectAssign() {
    if (Object.assign) {
      return Object.assign.apply(null, arguments);
    }

    var target = {};
    for (var index = 1; index < arguments.length; index++) {
      var source = arguments[index];
      if (source != null) {
        for (var key in source) {
          if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key];
          }
        }
      }
    }
    return target;
  }

  function getTimestamp(msec) {
    msec = !msec && msec !== 0 ? msec : 1;
    return parseInt(new Date().valueOf() / msec, 10);
  }

  // 降级方案
  function normalizeFallbackConfig(customConfig) {
    var siteProtocol = window.location.protocol.replace(":", "");
    var defaultConf = {
      protocol: siteProtocol === "http" ? "http" : "https",
      lang: "zh-CN",
      errorFallbackCount: 3,
    };
    var config = ObjectAssign({}, defaultConf, customConfig);

    var errorFallbackCount = config.errorFallbackCount;
    assert(
      errorFallbackCount === undefined ||
        (isInteger(errorFallbackCount) && errorFallbackCount >= 1),
      "errorFallbackCount must be an integer, and it's value greater than or equal one"
    );

    return config;
  }

  function loadResource(config, cb) {
    if (window.initNECaptcha) {
      setTimeout(function () {
        cb(null);
      }, 0);
      return;
    }
    function genUrl(server) {
      var path = "load.min.js";
      var urls = [];
      if (isArray(server)) {
        for (var i = 0, len = server.length; i < len; i++) {
          urls.push(joinUrl(config.protocol, server[i], path));
        }
      } else {
        var url = joinUrl(config.protocol, server, path);
        urls = [url, url];
      }

      return urls;
    }
    var urls = genUrl(
      config.staticServer || [
        "cstaticdun.126.net",
        "cstaticdun1.126.net",
        "cstatic.dun.163yun.com",
      ]
    );

    function step(i) {
      var url = urls[i] + "?v=" + getTimestamp(CACHE_MIN);
      loadScript(url, function (err) {
        if (err || !window.initNECaptcha) {
          // loadjs的全局变量
          i = i + 1;
          if (i === urls.length) {
            return cb(
              new Error(
                "Failed to load script(" +
                  url +
                  ")." +
                  (err ? err.message : "unreliable script")
              )
            );
          }
          return step(i);
        }
        return cb(null);
      });
    }
    step(0);
  }

  /*
   * entry: initNECaptchaWithFallback
   * options:
   *  errorFallbackCount: 触发降级的错误次数，默认第三次错误降级
   *  defaultFallback: 是否开启默认降级
   *  onFallback: 自定义降级方案，参数为默认validate
   */
  function initNECaptchaWithFallback(options, onload, onerror) {
    var captchaIns = null;

    var config = normalizeFallbackConfig(options);
    var defaultFallback = config.defaultFallback !== false;
    var langPkg = FALLBACK_LANG[config.lang === "zh-CN" ? config.lang : "en"];
    var storeKey =
      window.location.pathname +
      "_" +
      config.captchaId +
      "_NECAPTCHA_ERROR_COUNTS";
    try {
      errorCallbackCount = parseInt(localStorage.getItem(storeKey) || 0, 10);
    } catch (error) {}

    var fallbackFn = !defaultFallback
      ? config.onFallback || function () {}
      : function (validate) {
          function setFallbackTip(instance) {
            if (!instance) return;
            setFallbackTip(instance._captchaIns);
            if (!instance.$el) return;
            var tipEles = queryAllByClassName(
              ".yidun-fallback__tip",
              instance.$el
            );
            if (!tipEles.length) return;

            // 确保在队列的最后
            setTimeout(function () {
              for (var i = 0, l = tipEles.length; i < l; i++) {
                setDomText(tipEles[i], langPkg);
              }
            }, 0);
          }
          setFallbackTip(captchaIns);

          config.onVerify && config.onVerify(null, { validate: validate });
        };
    var noFallback = !defaultFallback && !config.onFallback;

    var proxyOnError = function (error) {
      errorCallbackCount++;
      if (errorCallbackCount < config.errorFallbackCount) {
        try {
          localStorage.setItem(storeKey, errorCallbackCount);
        } catch (err) {}

        onerror(error);
      } else {
        fallbackFn(DEFAULT_VALIDATE);
        proxyRefresh();
        noFallback && onerror(error);
      }
    };

    var proxyRefresh = function () {
      errorCallbackCount = 0;
      try {
        localStorage.setItem(storeKey, 0);
      } catch (err) {}
    };

    var triggerInitError = function (error) {
      if (initialTimer && initialTimer.isError()) {
        initialTimer.resetError();
        return;
      }
      initialTimer && initialTimer.resetTimer();
      noFallback ? onerror(error) : proxyOnError(error);
    };

    config.onError = function (error) {
      if (initialTimer && initialTimer.isError()) {
        initialTimer.resetError();
      }
      proxyOnError(error);
    };
    config.onDidRefresh = function () {
      if (initialTimer && initialTimer.isError()) {
        initialTimer.resetError();
      }
      proxyRefresh();
    };

    var initialTimer = options.initTimeoutError
      ? options.initTimeoutError(proxyOnError)
      : null; // initialTimer is only for mobile.html

    var loadResolve = function () {
      window.initNECaptcha(
        config,
        function (instance) {
          if (initialTimer && initialTimer.isError()) return;
          initialTimer && initialTimer.resetTimer();
          captchaIns = instance;
          onload && onload(instance);
        },
        triggerInitError
      );
    };
    var cacheId = "load-queue";
    if (!RESOURCE_CACHE[cacheId]) {
      RESOURCE_CACHE[cacheId] = {
        rejects: [],
        resolves: [],
        status: "error",
      };
    }
    if (RESOURCE_CACHE[cacheId].status === "error") {
      RESOURCE_CACHE[cacheId].status = "pending";
      loadResource(config, function (error) {
        if (error) {
          var err = new Error();
          err.code = REQUEST_SCRIPT_ERROR;
          err.message = config.staticServer + "/load.min.js error";

          var rejects = RESOURCE_CACHE[cacheId].rejects;
          for (var i = 0, iLen = rejects.length; i < iLen; i++) {
            rejects.pop()(err);
          }
          RESOURCE_CACHE[cacheId].status = "error";
        } else {
          RESOURCE_CACHE[cacheId].status = "done";
          var resolves = RESOURCE_CACHE[cacheId].resolves;
          for (var j = 0, jLen = resolves.length; j < jLen; j++) {
            resolves.pop()();
          }
        }
      });
    } else if (RESOURCE_CACHE[cacheId].status === "done") {
      loadResolve();
    }
    if (RESOURCE_CACHE[cacheId].status === "pending") {
      RESOURCE_CACHE[cacheId].rejects.push(function loadReject(err) {
        triggerInitError(err);
      });
      RESOURCE_CACHE[cacheId].resolves.push(loadResolve);
    }
  }

  return initNECaptchaWithFallback;
});
